'''
Created on 29 Nov 2012

@author: quirino.brizi
'''
from .connection import Connection, AsyncHTTPConnection
from .exceptions import PoolShutdownException, EmptyPoolException
from Queue import LifoQueue, Empty, Full
from time import sleep
from threading import Thread

class ConnectionPool(object):
    '''
    Defines the archetype for a connection pool concrete implementation
    '''

    LQeue = LifoQueue

    def __init__(self, host, port=None):
        '''
        Creates a basic ConnectionPool
        '''
        self.host = host
        self.port = port if port is not None else 80
    
    def _getconn(self):
        """
        Return a valid connection from the internal queue
        """
        raise "Not implemented yet"
    def _putconn(self, conn):
        """
        Put a connection back on the internal queue
        @param conn: the connection
        """
        raise "Not Implemented Yes"
    
class HTTPConnectionPool(ConnectionPool):
    
    def __init__(self, host=None, port=None, maxsize=10, block=False, 
                 timeout=10):
        """
        Initializes the HTTPConnectionPool
        @param host: the remote host name
        @param port: the remote host port
        @param maxsize: the maximum pool size
        @param block: pool blocking policy definition, True the pool will wait 
        if a connection is not available, False the request is not performed if 
        a connection is not available
        @param timeout: the timeout the pool should wait for a connection in 
        there internal queue is empty
        """
        super(HTTPConnectionPool, self).__init__(host, port)
        self.pool = self.LQeue(maxsize=maxsize)
        self.block = block
        self.timeout = timeout
        self.maxsize = maxsize
        # the number of active connection on the pool mainly for testing 
        # and debug
        self.active = 0
        # Fill the queue with None object so we are not sure the first 
        # request is no blocking if block is required
        for _ in xrange(maxsize): self.pool.put(item=None, block=False)
        self.need_init = False
        self.shutting_down = False
        self.hdr_out = None
        self.body_out = None
    
    def __del__(self): 
        self.close()
    
    def _getconn(self):
        """
        Gets a new connection from the pool
        @return: a Curl object
        """
        conn = None
        try :
            conn = self.pool.get(block=self.block, timeout=self.timeout)
            self.pool.task_done()
        except Empty, e :
            raise EmptyPoolException(e[0], e[1])
        # Create a new connection if and only if the number of active 
        # connections doesn't exceed the max allowed
        if conn is None and self.active < self.maxsize :
            self.active += 1
            conn = Connection()
        # reset the current connection so we are always using a brand new one
        conn.reset()
        return conn
    
    def _putconn(self, conn):
        """
        Put the connection back on the pool
        @param conn: the connection to have to back on the pool
        """
        try :
            self.pool.put(item=conn, block=False)
        except Full :
            pass
        finally:
            self.active -= 1
    
    def close(self):
        """
        Close the current pool and release all the resources, all the 
        connection will be closed and removed from the queue. That means it 
        will be not reusable.
        """
        # the internal queue so we are not blocking
        self.shutting_down = True
        queue = getattr(self.pool, 'queue');
        for conn in queue :
            if conn is not None : conn.close()
        del queue[:]
        self.active = 0
        self.need_init = True

    def urlopen(self, request):
        """
        Performs the request to the remote server and prepare the Response
        @param method: the request method as defined on HTTP protocol 
        specification
        @param url: the remote resource locator
        @param params: the query string parameters as a dictionary
        @param kwargs: additional configuration parameters
        """
        if self.shutting_down is True : raise PoolShutdownException(1, 
                        "Unable to send the request pool is shutting down")
        resp = None
        conn = self._getconn();
        try :
            resp = conn.exchange(request)
        finally:
            self._putconn(conn) 
        return resp

class AsyncHTTPConnectionPool(HTTPConnectionPool):
    """
    Asynchronous HTTP connection pool implementation
    """
    def __init__(self, host=None, port=None, maxsize=10, block=True, 
                 timeout=10):
        super(AsyncHTTPConnectionPool, self).__init__(
                                        host, port, maxsize, block, timeout)
    
    def urlopen(self, requests=[]):
        reqs = requests
        available = self.maxsize-self.active
        if available < len(requests): reqs = requests[0:available]
        connections = []
        for request in reqs:
            conn = Connection()
            conn.prepare(request)
            connections.append(conn)
        
        async = AsyncHTTPConnection(connections)
        futures = async.exchange()
        thread = Thread(target=self._checker, args=(async, connections),
                        name="AsyncHTTPConnectionPool checker")
        thread.start()
        return futures
    
    def _checker(self, async, connections):
        while not async.isdone(): sleep(0.1)
        for conn in connections: self._putconn(conn)